/**  
 * @Title: JobService.java
 * @Description: TODO(描述)
 * @author liuyi
 * @date 2023-06-26 08:30:04 
 */  
package com.fsxgt.datagrid.console.service.impl;

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.support.CronExpression;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.fsxgt.datagrid.component.bo.DataWorkBO;
import com.fsxgt.datagrid.component.dao.DataWorkMapper;
import com.fsxgt.datagrid.console.bo.FlowExecLogBO;
import com.fsxgt.datagrid.console.bo.JobInfoBO;
import com.fsxgt.datagrid.console.dao.FlowExecLogMapper;
import com.fsxgt.datagrid.console.dao.JobInfoMapper;
import com.fsxgt.datagrid.console.exception.RunningException;
import com.fsxgt.datagrid.flow.bo.FlowInfoBO;
import com.fsxgt.datagrid.flow.bo.FlowMenuBO;
import com.fsxgt.datagrid.flow.bo.SelectBO;
import com.fsxgt.datagrid.flow.dao.FlowInfoMapper;
import com.fsxgt.datagrid.flow.dao.FlowMenuMapper;
import com.fsxgt.datagrid.flow.service.impl.FlowMenuService;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

/**  
 * @ClassName: JobService
 * @Description: TODO(描述)
 * @author liuyi
 * @date 2023-06-26 08:30:04 
*/
@Service
@Slf4j
public class JobService {
	
	
	
	@Autowired
	private ScheduledHelper scheduledHelper;
	
	@Autowired
	private JobInfoMapper jobInfoMapper;
	@Autowired
	private FlowInfoMapper flowInfoMapper;
	@Autowired
	private DataWorkMapper dataWorkMapper;
	
	@Autowired
	private FlowMenuMapper flowMenuMapper;
	@Autowired
    private FlowExecLogMapper flowExecLogMapper;
	
	@Autowired
    private FlowMenuService flowMenuService;
	
	
	
	@Autowired
	private TaskService taskService;
	
	@Value("${test}")
	private Boolean test;
	
	@PostConstruct
	private void init() {
		
		
		List<JobInfoBO> list = jobInfoMapper.findOpenJobList();
		for(JobInfoBO bo:list) {
			if(bo.getParentJobId()==null) {
				addTask(bo);
			}
			
		}

	}
	
	
	

	/**  
	 * @Title: saveJob
	 * @Description: TODO(描述)
	 * @param jobInfoBO
	 * @author liuyi
	 * @date 2023-06-26 08:52:33 
	 */  
	public void addJob(JobInfoBO jobInfoBO) {
		// TODO Auto-generated method stub
		Date cur = new Date();
		jobInfoBO.setAddTime(cur);
		jobInfoBO.setUpdateTime(cur);
		jobInfoBO.setStatus("0");
		jobInfoBO.setJobType("1");
		if(jobInfoBO.getTriggerType()!=null&&jobInfoBO.getTriggerType().equals("2")) {
			jobInfoBO.setJobCron(null);
		}else {
			jobInfoBO.setParentJobId(null);
		}
		
		if(jobInfoBO.getObjType().equals("1")) {
			jobInfoBO.setFlowRootId(flowMenuService.getFlowRootId(jobInfoBO.getObjId()));
		}
		
	
		
		
		
		jobInfoMapper.insertJobInfo(jobInfoBO);
	}

	/**  
	 * @Title: modifyJob
	 * @Description: TODO(描述)
	 * @param jobInfoBO
	 * @author liuyi
	 * @date 2023-06-26 08:55:26 
	 */  
	public void modifyJob(JobInfoBO jobInfoBO) {
		// TODO Auto-generated method stub
		jobInfoBO.setUpdateTime(new Date());
		if(jobInfoBO.getTriggerType()!=null&&jobInfoBO.getTriggerType().equals("2")) {
			jobInfoBO.setJobCron(null);
		}else {
			jobInfoBO.setParentJobId(null);
		}
		
		if(jobInfoBO.getObjType().equals("1")) {
			jobInfoBO.setFlowRootId(flowMenuService.getFlowRootId(jobInfoBO.getObjId()));
		}
		
		jobInfoMapper.updateJobInfo(jobInfoBO);
		
		JobInfoBO bo = jobInfoMapper.getJobInfo(jobInfoBO.getId());
		if(bo.getStatus().equals("1")) {
			removeTask(bo);
			addTask(bo);
			
		}
	}

	/**  
	 * @Title: deleteJob
	 * @Description: TODO(描述)
	 * @param jobInfoBO
	 * @author liuyi
	 * @date 2023-06-26 08:56:05 
	 */  
	public void deleteJob(Long id) {
		// TODO Auto-generated method stub
		closeJob(id);
		jobInfoMapper.deleteJobInfo(id);
		flowExecLogMapper.deleteFlowExecLog(id);
	}

	/**  
	 * @Title: openJob
	 * @Description: TODO(描述)
	 * @param jobInfoBO
	 * @author liuyi
	 * @date 2023-06-26 09:18:02 
	 */  
	@Transactional(rollbackFor = Exception.class)
	public void openJob(Long id) {
		// TODO Auto-generated method stub
		JobInfoBO bo = jobInfoMapper.getJobInfo(id);
		bo.setStatus("1");
		jobInfoMapper.updateJobInfo(bo);
		
		addTask(bo);
		
	}
	
	public void removeTask(JobInfoBO bo) {
		if(bo.getParentJobId()==null) {
			scheduledHelper.removeCronTask(bo.getId().toString());
		}else {
			Map<String, Runnable> map = scheduledHelper.getRunnableMap();
			Map<Long,JobInfoBO> jobMap = new HashMap();
			map.forEach((k,v)->{
				jobMap.put(Long.valueOf(k), ((JobRunnable)v).getJobInfoBO());
			});
			updateSubTask(jobMap,bo,2);
		}
		
	}
	

	
	public void addTask(JobInfoBO bo) {
		
		
		if(test!=null&&test) {
			//测试
			if(bo.getJobName()!=null&&bo.getJobName().indexOf("test")!=0) {
				return;
			}
			
		}else {
			//生产
			if(bo.getJobName()!=null&&(bo.getJobName().indexOf("test!34532")==0)) {
				return;
			}
		}
		
		initJob(bo);
		
		if(bo.getParentJobId()==null) {
			scheduledHelper.addCronTask(bo.getId().toString(), new JobRunnable(bo), bo.getJobCron());
		}else {
			Map<String, Runnable> map = scheduledHelper.getRunnableMap();
			
			Map<Long,JobInfoBO> jobMap = new HashMap();
			
			map.forEach((k,v)->{
				jobMap.put(Long.valueOf(k), ((JobRunnable)v).getJobInfoBO());
			});
			updateSubTask(jobMap,bo,1);
			
		}
		
	}

	
	private void updateSubTask(Map<Long,JobInfoBO> jobMap,JobInfoBO bo,int type) {
		
		//type  1添加  2删除
		
		JobInfoBO job = jobMap.get(bo.getParentJobId());
		if(job!=null) {
			if(job.getSubJobMap().containsKey(bo.getId())) {
				job.getSubJobMap().remove(bo.getId());
			}
			if(type==1) {
				job.getSubJobMap().put(bo.getId(), bo);
			}
		}else {
			jobMap.values().forEach(v->{
				if(!v.getSubJobMap().isEmpty()) {
					updateSubTask(v.getSubJobMap(),bo,type);
				}
			});
		}
		
	}
	@Data
	private class JobRunnable implements Runnable{

		public JobInfoBO jobInfoBO;
		public JobRunnable(JobInfoBO jobInfoBO) {
			this.jobInfoBO=jobInfoBO;
		}
		
		@Override
		public void run() {
			// TODO Auto-generated method stub
			try {
				if(jobInfoBO.getErrorContinue()!=null&&jobInfoBO.getErrorContinue().equals("0")&&jobInfoBO.getErrorStatus()==1) {
					;
				}else {
					log.info("触发job:"+jobInfoBO.getId());
					taskService.startTask(jobInfoBO, new Date());
				}
				
			} catch (RunningException e) {
				// TODO Auto-generated catch block
				log.warn("",e);
			}
			log.info(Thread.currentThread().getName()+":"+jobInfoBO.getJobDesc());
		}
		
	}

	/**  
	 * @Title: closeJob
	 * @Description: TODO(描述)
	 * @param jobInfoBO
	 * @author liuyi
	 * @date 2023-06-26 09:18:12 
	 */  
	@Transactional(rollbackFor = Exception.class)
	public void closeJob(Long id) {
		// TODO Auto-generated method stub
		JobInfoBO bo = jobInfoMapper.getJobInfo(id);
		bo.setStatus("0");
		jobInfoMapper.updateJobInfo(bo);
		
		removeTask(bo);
	}

	public void execJob(Long id) throws RunningException {
		JobInfoBO bo = jobInfoMapper.getJobInfo(id);
		initJob(bo);
		boolean b=taskService.startTask(bo, new Date());
		if(!b) {
			throw new RunningException("启动失败");
		}
	}
	
	public void initJob(JobInfoBO bo) {
		
		Set<Long> set=new HashSet();//防止递归死循环
		

		initJob(bo,set);
			
	}
	
	private void initJob(JobInfoBO bo,Set<Long> set) {
		
		set.add(bo.getId());
		
		SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		bo.setVersion(formatter.format(new Date()));
		
		JobInfoBO param=new JobInfoBO();
		param.setParentJobId(bo.getId());
		param.setStatus("1");
		
		List<JobInfoBO> list = jobInfoMapper.findJobInfoList(param);
		for(JobInfoBO job:list) {
			if(!set.contains(job.getId())) {
				set.add(job.getId());
				bo.getSubJobMap().put(job.getId(),job);
				initJob(job,set);
			}
		}
		
	}
	
	
	
	

	
	
	public List<String> getNextTime(Long id){
		
		JobInfoBO bo = jobInfoMapper.getJobInfo(id);
		
		List<String> result=new ArrayList();
		DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
		CronExpression cronTrigger = CronExpression.parse(bo.getJobCron());
		
		Date date=new Date();
	    for(int i=0;i<5;i++) {
		   LocalDateTime gmt = LocalDateTime.ofInstant(date.toInstant(), ZoneId.ofOffset("GMT", ZoneOffset.ofHours(8)));
		   LocalDateTime next = cronTrigger.next(gmt);
		   result.add(format.format(next));
		   date=Date.from(next.atZone(ZoneId.ofOffset("GMT", ZoneOffset.ofHours(8))).toInstant());
	    }
	    
		return result;
		
	}
	
	public List<SelectBO> getFlowInfo(){
		
		List result=new ArrayList();
		
		List<FlowInfoBO> flowList = flowInfoMapper.findFlowInfoAll();

		List<FlowMenuBO> menuList = flowMenuMapper.findMenu();
		
		
		
		for(FlowInfoBO flow:flowList) {
			SelectBO select = new SelectBO(flow.getId(),getFlowInfoPath(flow.getName(),flow.getFlowMenuId(),menuList));
			result.add(select);
		}
		
		return result;
		
	}
	
	/**  
	 * @Title: getDataWork
	 * @Description: TODO(描述)
	 * @return
	 * @author liuyi
	 * @date 2023-09-13 11:14:21 
	 */  
	public List<SelectBO> getDataWork() {
		// TODO Auto-generated method stub
		
		List result=new ArrayList();
		
		List<DataWorkBO> list = dataWorkMapper.find(null);
		for(DataWorkBO bo:list) {
			SelectBO select = new SelectBO(bo.getId(),bo.getName());
			result.add(select);
		}
		
		return result;
	}
	
	
	public String getFlowInfoPath(String flowInfoName,String flowParentId,List<FlowMenuBO> menuList){
		
		
		Map<String, FlowMenuBO> menuMap = menuList.stream()
				.collect(Collectors.toMap(bo -> (bo.getId()), bo -> (bo)));
		
		
		String name=flowInfoName;
		
		String parentId=flowParentId;
		while(parentId!=null&&menuMap.get(parentId)!=null) {
			name=menuMap.get(parentId).getName()+"/"+name;
			parentId=menuMap.get(parentId).getParentId();
		}

		
		return name;
	}
	
	

	public List<JobInfoBO> findJobInfoPage(JobInfoBO jobInfoBO) {
		
		List<JobInfoBO> list = jobInfoMapper.findJobInfoPage(jobInfoBO);
	
		
		return list;
	}
	
    public List<JobInfoBO> findSelectJobList(Long jobId) {
		
		List<JobInfoBO> list = jobInfoMapper.findSelectJobList(jobId);
	
		return list;
	}



	public List<FlowExecLogBO> findLogPage(FlowExecLogBO flowExecLogBO) {
		
		List<FlowExecLogBO> list = flowExecLogMapper.findLogList(flowExecLogBO);
	
		
		return list;
	}


	/**  
	 * @Title: getJob
	 * @Description: TODO(描述)
	 * @param jobInfoBO
	 * @author liuyi
	 * @date 2023-08-10 11:44:42 
	 */  
	public JobInfoBO getJob(JobInfoBO jobInfoBO) {
		// TODO Auto-generated method stub
		return jobInfoMapper.getJobInfo(jobInfoBO.getId());
	}


	


}
